/* qBase.js (qJSLib)

    @Summary    : OOP, Client and Data Mgmt
    @Author     : Timothy C. Quinn
    @License    : http://www.opensource.org/licenses/mit-license.php

    @Dependencies:
     - qCore.js (qJSLib)

    @Notes:
     - Source includes Base.js with few customizations to original sources

    @Todo:
     - Analyse why caller crawl does not work with base() call
     - Generisize isLoggedIn() to be configurable
*/


/*
    Base, version 1.0.2
    Copyright 2006, Dean Edwards
    License: http://creativecommons.org/licenses/LGPL/2.1/
    :: Changes ::
    . Renamed to QBase
    :: Known Subclasses ::
    . QBase*
        |__QEventDomain
            |__QWidget
                |__ ...
            |__QClient
                |__ ...
            |__...
*/

var QBase = function() {
    if (arguments.length) {
        if (this == window) { // cast an object to this class
            QBase.prototype.extend.call(arguments[0], arguments.callee.prototype);
        } else {
            this.extend(arguments[0]);
        }
    }
};

QBase.version = "1.0.2";

QBase.prototype = {
    extend: function QBase_extend(source, value) {
        var extend = QBase.prototype.extend;
        if (arguments.length == 2) {
            var ancestor = this[source];
            // overriding?
            if ((ancestor instanceof Function) && (value instanceof Function) &&
                ancestor.valueOf() != value.valueOf() && /\bbase\b/.test(value)) {
                var method = value;
            //  var _prototype = this.constructor.prototype;
            //  var fromPrototype = !QBase._prototyping && _prototype[source] == ancestor;
                value = function() {
                    var previous = this.base;
                //  this.base = fromPrototype ? _prototype[source] : ancestor;
                    this.base = ancestor;
                    var returnValue = method.apply(this, arguments);
                    this.base = previous;
                    return returnValue;
                };
                // point to the underlying method
                value.valueOf = function() {
                    return method;
                };
                value.toString = function() {
                    return String(method);
                };
            }
            return this[source] = value;
        } else if (source) {
            var _prototype = {toSource: null};
            // do the "toString" and other methods manually
            var _protected = ["toString", "valueOf"];
            // if we are prototyping then include the constructor
            if (QBase._prototyping) _protected[2] = "constructor";
            for (var i = 0; (name = _protected[i]); i++) {
                if (source[name] != _prototype[name]) {
                    extend.call(this, name, source[name]);
                }
            }
            // copy each of the source object's properties to this object
            for (var name in source) {
                if (!_prototype[name]) {
                    extend.call(this, name, source[name]);
                }
            }
        }
        return this;
    }

    ,base: function QBase_base() {
        // call this method from any other method to invoke that method's ancestor
    }
  // Dynamic Inheritance - By Instance
    ,inherit: function QBase_inherit(obj) { qObjInherit(this, obj) }
    
//    ,regConst: function QBase_regConst(jConst, oInst){
//    	var t=this;
//    	t.inherit(jConst)
//    	if(oInst
//    	
//    }
    
};
function qObjInherit(objBase, obj){
    var t=objBase,aMth=qObjListMethods(obj),aPrp=qObjListProps(obj),v
    for(var i=0;i<aMth.length;i++){v=aMth[i];if(!__QBase_InheritIgnoreMth.has(v)){t[v]=obj[v]}}
    for(var i=0;i<aPrp.length;i++){v=aPrp[i];if(!__QBase_InheritIgnoreProps.has(v)){t[v]=obj[v]}}
}
__QBase_InheritIgnoreMth=["constructor","base_constr"]
__QBase_InheritIgnoreProps=["name","id"]


QBase.extend = function QBase_extend(_instance, _static) {
    var extend = QBase.prototype.extend;
    if (!_instance) _instance = {};
    // build the prototype
    QBase._prototyping = true;
    var _prototype = new this;
    extend.call(_prototype, _instance);
    var constructor = _prototype.constructor;
    _prototype.constructor = this;
    delete QBase._prototyping;
    // create the wrapper for the constructor function
    var klass = function() {
        if (!QBase._prototyping) constructor.apply(this, arguments);
        this.constructor = klass;
    };
    klass.prototype = _prototype;
    // build the class interface
    klass.extend = this.extend;
    klass.implement = this.implement;
    klass.toString = function() {
        return String(constructor);
    };
    extend.call(klass, _static);
    // single instance
    var object = constructor ? klass : _prototype;
    // class initialisation
    if (object.init instanceof Function) object.init();
    return object;
};

QBase.implement = function QBase_implement(_interface) {
    if (_interface instanceof Function) _interface = _interface.prototype;
    this.prototype.extend(_interface);
};


/******************************************************
    Event Domain Object

    .: OOP Specs :.
    Constraints:
    . Many per single window
    Direct Known Subclasses:
    . QBase
        |__QEventDomain*
            |__QWidget
            |__QClient
            |__...
******************************************************/
QEventDomain__firstInstance=true
QEventDomain__staticReg={
    instancePool: {}
}
QEventDomain = QBase.extend({// Abstract class
  // Constructor
    base_constr :function evndomain_base_constr(instance, name){
//        pc(">>");
        var t=this, reg=_w.QEventDomain__staticReg,sRand
        try {
          // Initialize Widget Registry
            if(_w.QEventDomain__firstInstance){
                _w.QEventDomain__firstInstance=false
                //... do stuff on first call
            }
            
            t.name=name
            t.instance = instance

          // Register Intances
            while(true){
                sRand=nRand(6)
                if(!reg.instancePool[sRand]){
                    instance.instanceId=sRand
                    reg.instancePool[sRand]={
                         obj            :instance
                        ,name           :name
                        ,instanceId     :sRand
                    }
                    break
                }
            }
        }catch(exc){
            t.loadHadCoreError=true
            qErr(
                "QEventDomain Base Constructor Failed"
                ,exc
                ,{
                     t          :t
                    ,rawStack   :(is.moz?"\n"+exc.stack:"(na - isie)")
                }
            )
        }

//        pc("<<")
    }

  // Instance Properties
    ,instanceId  :null


  /***********************
    Event Model

    :: Add Event ::
    . For adding events to elements
    . Must be called from within parent object (not globally)

    :: Params ::
    . oE: Element or Element ID
    . sE: Event Type. eg. click, mouseover...
    . fH: Handler
    . qOpt: QOPT JSON args
    . . |--: ... (tbd)

  ************************/

//    ,priv_eventCache :{}
    ,addEvent :function qclient_addEvent(oE,sE,fH,qOpt){//debug("called");
        var t=this,sH, oMH,aVEv,sSEv,oSEv
            ,sId=(oE?oE.id:null),v
            ,bIsCore=(oE==_w||oE==_d.body)
        try {
          // Parse Params
            if(isEvent(t)||!t){__e("FATAL - Cannot bind events from within an event or globally!!!")}
            if(oE==undefined){__e("oE must be elem -or- window -or- body")}
            if(oE==_d){__e("oE cannot be _d")}
            if(!bIsCore&&(!isElem(oE)||isUndef((sId=oE.id))||sId=="")){__e("oE must be an elem with a unique ID")}
            if(!isFunc(fH)){__e("fH must be a function")}
            qOpt=isQOpt(qOpt)?qOpt:{}
//if(oE.id=="~someBOID~")
//	pc(
//	    "\n oE = "+oE
//	   +"\n sE = "+sE
//	   +"\n fH = "+fH
//	   +"\n qOpt = "+qOpt
//	   +"\n stack:\n"+s$.pad(getStackTrace())
//	)
            aVEv=["focus","blur","click","mouseover","mouseout","mouseup", "mousedown","keydown","keypress","change","resize","loaded","unload"]; if(!aVEv.has(sE)){qErr("sE(~1) is invalid it must be one of : \n~2".format(sE,aVEv.join(", ")))}
            sFBody="var bRet, oS;\n try {if(is.ie){e=getEvent(e);\n if(e==null){return}};\n oS=~1;\n if(!oS){return};\n  \n e.qSource=oS;\n _qclient.priv_masterEventListener(e);\n }catch(x){qErr(x, {varDump: {event: e, source: e.source, qSource: e.qSource, target: (v=e.target)?v.tagName+' - '+v.id:'none'}})}".format((
                (oE==_w&&isDef(oE.getElem)?
                    "window":
                    (oE==_d.body?
                        "_d.body":
                        "getElem('"+oE.id+"')"
                    )
                )
            ))

            is.ie?oE["on"+sE]=new Function("e",sFBody):oE.addEventListener(sE,new Function ("e",sFBody),true)

            oE["jsParent"]=t /*Pointer to qclient*/

          // Other
            oE["_on"+sE]=fH
//            t.priv_eventCache[sId]=fH

        }catch(x){qErr(x,{varDump:{args: q$.dumpArgs()}})}
        function __e(s){qErr("addEvent Failed. Reason: "+s)}
    }
    ,priv_masterEventListener :function qclient_masterEventListener(oEvn){
//        pc(">>");
        var t=_qclient, oS=oEvn.source||oEvn.qSource,sE=oEvn.type,oSE,sId=(oS?oS.id:null),f,s

        if(!oEvn.cancel){oEvn.cancel=function evn_cancel(){this.isCancelled=true}}
//pc(qDumpObj(oEvn))

        //f=t.priv_eventCache[sId]
        oSE=oS["_on"+sE]
//pc(
//  "\n oS = "+oS
//   +"\n sE = "+sE
//   +"\n sId = "+sId
//   +"\n oEvn = "+oEvn
//   +"\n _qclient.qcGate(oEvn) = "+_qclient.qcGate(oEvn)
//   +"\n oSE = "+oSE
//)
        if(!oEvn||!oS||!oSE){
//            pc("Killing Event: oEvn: ~1, oS: ~2, oSE: ~3".format(oEvn, oS, oSE));
            return
        }
    //pc("~1 | ~2 | ~3 | ~4".format(oS,sE,oSE,oS.innerHTML))
        try {
            if(!_qclient.qcGate(oEvn))return

            try{
                oSE(oEvn);
//              if(oEvn.isCancelled){// could not get this working :(
//                   if (oEvn.stopPropagation)oEvn.stopPropagation();
//                   if (oEvn.preventBubble)oEvn.preventBubble();
//                   if (oEvn.preventDefault)oEvn.preventDefault();
//                   oEvn.cancelBubble=true
//              }

            }catch(e){ qErr((is.ie?("Source: "+funcName(oSE)):"Func Name: "+funcName(oSE)),e) }
        }catch(e){
            if(is.moz){window.onerror(e.message,_w.location.href,-1)}else{qErr(e)}
        }
//      pc("<<")
    }
    ,preListener_priv :function qclient_preListener_priv(e){
        if(_w.bModalWait){
            try{testModalDialog()}catch(x){}
            if(isEvent(e)){
                var oT=e.target
                if(is.ie){
                    if(e.type=="mousedown"&&isDD(oT)&&oT.id!=""&&oT.enabled==true){
                        oT.disabled=true
                        setTimeout("getElem('"+oT.id+"').disabled=false",200) 
                    }
                }else{
                    e.stopPropagation()
                }
            }
        	return false
        }
        return true
    }
    ,rfcLoadingGate :false
    ,qcGate :function QC_qcGate(oEvn){
        var oH,oNav,t=_qclient, h
        try{
            if(isFunc(h=t.localQCGate)){if(!h(oEvn)){return false}}
            if(t.rfcLoadingGate){__();return false}
            if(!t.preListener_priv(oEvn)){__();return false}
            return true
        }catch(e){qErr(e)}
        function __(){
        	try {
        		pc("qcGate() caught!\n"+q$.getCallerInfo())
        	}catch(x){pc("DBG FAILED - x = "+x)}
        }
    }
    ,rfcLoadingStart :function QC_rfcLoadingStart(){
        _qclient.rfcLoadingGate=true
    }
    ,rfcLoadingStop :function QC_rfcLoadingStop(){
        _qclient.rfcLoadingGate=false
    }

})






/*******************************************************
    Client Base Class

    :: OOP Specs ::
    ::  . Constraints ::
        . Single Instnace - only one Client per window
    :: Direct Known Subclasses ::
        . Base
            |__QEventDomain
                |__QClient*
                    |__MapCore
    Notes:
    . ctor Args:
    .. {
    ..   name       :"<Name of QC>"
    ..  ,qcName     :"<Global Name of QC>"
    ..  ,instance   :<final Instance>
    .. }
    . There must be at least one QClient defined in the window
    . To initialize Default QClient use __initDefQClient()

    Todo:
     - Create Dynamic JS Loading Framework
     -- Allow for loading of JS Files on-the-fly
     -- Have Loading library keep an eye on loading time and err on timeouts
     -- Have callback on completion to continue user thread(s)

*******************************************************/

// Each window should only have one Instance of QClient
QClient_config = {
  // This should be set before ctor call of qclient
	contextCheckServletPath: ""//"/webres/common/iDash_isLoggedIn.jsp"
}

QClient = QEventDomain.extend({// Abstract class

  // Constructor

     base_constr :function qclient_base_constr(jArgs){//alert("qclient_base_constr called")
//        pc(">>")
        var t=this,instance, name, sQCName;
        jArgs=jArgs?jArgs:{}
        try {
//pc(q$.dumpArgs())

          // Parse Args
            instance = agetParam_obj("Ctor jArgs", jArgs, "instance", true);
            name = agetParam("Ctor jArgs", jArgs, "name", true, true);
            sQCName = agetParam("Ctor jArgs", jArgs, "qcName", true, true);
            t.base(instance, name)

          // Declare Global namespace for Singleton Instance of Client
            _w._qclient=_w[sQCName]=instance

          // Init Instance Vars
            t.name = name
            t.qcName=sQCName

        }catch(exc){
            t.loadHadCoreError=true
            qErr(
                "QClient Base Constructor Failed"
                ,exc
                ,{argDump    :q$.dumpArgs}
            )
        }
//        pc("<<")
    }

// Constants
// . General
    ,SERVER_TYPE_PROD   : "SYSTEM_IS_PROD"
    ,SERVER_TYPE_STAGE  : "SYSTEM_IS_STAGE"
    ,SERVER_TYPE_DEV    : "SYSTEM_IS_DEV"
    ,SERVER_TYPE_UNKNOWN: "SYSTEM_IS_UNKNOWN"
    ,OA_CTX_MODE: "contextMode"

    ,MESSAGE_ABSTRACT_METHOD: "This method must be overridden to be properly utilized!"
// . Debug
    ,debugVars : [["General"],["oURI","URI Object"],["imagePath","Image Path"],["wrroot","Web Resource Root"],["scriptPath","Script Path"],["serverPath","Server Path"]]


// . HTML
    ,HTML_LOADING   :"<table id=tblLoadingBlock height=100% align=center'><tr><td><img id=imgLoadingImage height=16px width=16px src='"+_w.imagePath+"spinner_small.gif' style='padding-top: 0px'></td><td id=tdLoadingText style='font: bold 16px arial; vertical-align: center; color: #888888'>&nbsp;&nbsp;~1</td></tr></table>"
    ,HTML_EXT_LINK :"<sup><img class='rendericon' width='7' height='7' border='0' align='absmiddle' alt='' src='"+_w.imagePath+"linkext.gif'/></sup>"
// Properties
// Get Properties (public read)
    ,errorCaptured  :false
    ,loadHadServerError :false
    ,qcName :null // Global name of client (variable of window)
    ,isProd         :false
    ,isStage        :false
    ,isDev          :false
    ,isUnknownST    :false

// Set and GetProperties (public read write once)
    ,toolLoadComplete :false

// Private
    ,serverType     :null
    ,isFirstRun     :true

  /*
    First Call of Program
    . This method must be called at the start load on change in client View
   */
    ,boot :function qc_boot(){
//        pc(">>")
        var t=this

		t.validateLoad(_w.ServerReport, _w.OtherAtts)

//        pc("<<")
    }

  /*
    View Load Start Handler
    . This method must be called at the start load on change in client View
   */
    ,viewLoadStart :function qc_viewLoadStart(){
//        pc(">>")
        var t=this
        t.viewLoadCompleteCallback = null
//pc("DEBUG - CLEARING WIN ONRESIZE EVENT");
//_w.onresize = null
//        pc("<<")
    }

  /*
    View Load Complete Handler
    . This method must be called last in any change in client View
   */
    ,viewLoadCompete :function qc_viewLoadCompete(){
//        pc(">>")
        var t=this,fCB
        fCB=t.viewLoadCompleteCallback
        t.viewLoadCompleteCallback=null
        t.isFirstRun=false;
        if(fCB){
            if(isFunc(fCB)){
                try { fCB(); } catch(x){qErr("Failed while calling Load Compete Callback (var=loadCompleteCallback, func="+funcName(fCB)+")",x)}
            } else {
                pc("callback in loadCompleteCallback var is not a function! loadCompleteCallback:\n"+s$.pad(qDumpObj(fCB)))
            }
        }
//        pc("<<")
    }


  /* Final Error Notifier
     . Must NOT be called directly from Bus Logic
     . Must be called from instance qClient class after appending client intance specific debug data to dd.
     .. call using this.priv_coreErrorNotifier(dd) from priv_errorNotifier
     . Only argument must be qDD
     TODO:
     . Find out how to call directly an not through window_onerror
     .. There are situations where an error will never get to window_onerror (eg. click events in GMaps)
     . Append General Client Info to dd
     . Capture error info and stream to server
     .. Get Console cache as well!
  */
    ,priv_errorHandlerCore :function qclient_priv_errorHandlerCore(dd){
        if(_w.bDebug)pc("CALLED")
        var t=_qclient,db=_d.body,dbs,sMsg,sOut,bHasRC

        if(!isQDD(dd))qCoreErr("Core Error. arg[0] is not qDD", new Error(),{varDump:{args: q$.dumpArgs()}});

        try {


            if(isDef(errRC=dd.rootCause)){ bHasRC=true}

          // Append General Client Info
            dd.append(dd.QCLIENT_INFO, "name = "+t.name)
            dd.append(dd.QCLIENT_INFO, "isProd = "+t.isProd)
            dd.append(dd.QCLIENT_INFO, "isStage = "+t.isStage)
            dd.append(dd.QCLIENT_INFO, "isDev = "+t.isDev)
            dd.append(dd.QCLIENT_INFO, "isUnknownST = "+t.isUnknownST)

          // Validate Error Context
            sMsg=dd.getSect(dd.MESSAGE)

          // Handle Special Error Classes
            if(errRC){
                if(_w.JSONRpcClient && (errRC.isInstanceOf(ERRCLS_JSONRpcClient_Exception) || errRC.isInstanceOf(ERRCLS_qRpcError))){//errRC.isJSONRpcClientException || false)){
                    dd.append(dd.QCLIENT_INFO, "Java Code = "+errRC.code)
                    dd.append(dd.QCLIENT_INFO, "Java Error Class = "+errRC.name)
                    var sJavaStack = errRC.javaStack, iFind1, iFind2
                    if(sJavaStack){
                        iFind1 = sJavaStack.indexOf("at ")
                        iFind2 = sJavaStack.indexOf("javax.servlet.http.HttpServlet.service", iFind1)
                        if(iFind2!=-1){
                            iFind2 = sJavaStack.indexOf("\n", iFind2+2)
                            if(iFind2!=-1){
                                sJavaStack = sJavaStack.substring(iFind1-1, iFind2);
                            }
                        }
                    }
                    dd.append(dd.QCLIENT_INFO, "Java Stack:\n"+s$.pad(sJavaStack))


                }
            }


          // Check for session RPC Error
            if(s$(sMsg).indexOf("session may have timed out")>-1){
              // Check if Session has Actually Expired!!
                if(t.user.isLoggedIn()){
                    pc("IS STILL LOGGED IN - LIKELY METHOD ISSUE!");
                } else {
                    pc("IS NOT LOGGED IN!");
                }

                if(!t.user.isLoggedIn()){
                    alert("Your session has expired. Reloading...");
                    /* TODO
                       . Make this a bit smarter by remembering where the user was and bring them right back after logging in
                       . Also consider showing Login window in a modal iframe popup. That way we don't have to refresh or rebuild client ;)
                     */
    //pc("TEMPORARILY TURNED OFF REFRESH WINDOW")
                    winQuickRefresh()
                }
            }

          // Append General qClient Info

          // Build Error Message
            sOut=dd.toString(QOpt({verbose: true, spaceSection: true}))

            if(bHasRC){
                pc(
                     "\n\n qClient ("+t.qcName+") Has Error with a Root Cause!"
            //                +"\n errRC = "+errRC
                    +"\n RC Class = "+errRC.getClassName()
                    +"\n DUMP:\n"+q$.dumpArgs()
                    +"\n Full Dump:\n"+s$.pad(sOut, ".  ")+"."
                    +"\n\n"
                );
            }

            if(!db){
                alert(sOut)
            } else {
                db.innerHTML="QClient Core Error<br><div id=divErr></div>"+(t.getErrorNote?t.getErrorNote():"")
                dbs=db.style
                dbs.color="navy"
                dbs.fontFamily="courier new"
                dbs.fontWeight="bold"
                dbs.fontSize="14px"
                dbs.margin="10px"
                sOut = s$(sOut).convlHTMLCharsToNorm().replace(/~#10~/g,"<br>")
                setTextNodeSplit("divErr", t.cleanCharsForDebug(sOut))
            }
            return _w.bDebug?false:true

        }catch(x){
            qErr(
                "CORE ERROR IN priv_coreErrorNotifier"
               ,x
               ,{origErr: s$.pad(q$.dumpArgs(), ". ")}
            )
            return false
        }
    }


  // Abstract Methods - Must be overridden
    // IS THIS STILL NEEDED?
    ,overrideMethod :function qclient_overrideMethod(oClient,sMthd){
        var t=_qclient
        if(!oClient[sMthd]){qErr("Method "+sMthd+" must be implemented by "+(typeof oClient))}
        t[sMthd]=oClient[sMthd]
    }
  // Methods
  // . Debug
    ,debugDump :function qclient_debugDump(sMsg,e){
        var t=_qclient;
        _w._DebugDumpMessage=sMsg;
        _w._DebugDumpError=e;
        _w.open(wrroot+"qDump.html?qcName="+t.qcName,"_blank","top=50,left=50,width=950,height=600,resizable=yes, menubar=yes,scrollbars=yes, status=yes, titlebar=no, toolbar=no")

    }
    ,appendDebugVars :function qclient_appendDebugVars(a){ try{var t=_qclient; t.debugVars=t.debugVars.concat(a)}catch(e){qErr(e)}}

    ,validateLoad :function _QC_validateLoad(SR,OA){
//        pc(">>")
        var t=_qclient;
        OA=OA?OA:{}
        if(!isJson(SR)){qErr("SR is not a JSON!")}
        if(!isJson(OA)){qErr("OA is not a JSON!")}
        try {
            t.serverType = SR["ServerType"] || t.SERVER_TYPE_UNKNOWN
            switch(t.serverType){
                case t.SERVER_TYPE_PROD     :t.isProd         =true; break
                case t.SERVER_TYPE_DEV      :t.isDev          =true; break
                case t.SERVER_TYPE_STAGE    :t.isStage        =true; break
                default                     :t.isUnknownST    =true; break
            }

        }catch(exc){
            qErr(exc)
        }
//        pc("<<")
    }


  // Server Report / Info parsing
    ,parseServerReport :function _QC_parseServerReport(SR){ if(isUndef(SR)){qErr("Server Report Not Defined!")}else{if(!isJson(SR)){SR=isStr(SR)?SR.split(s$.NV_DEL).toJson():SR.toJson();} _w.ServerReport=SR}; return SR }
    ,parseOtherAtts :function _QC_parseOtherAtts(OA){ if(isDef(OA)){if(!isJson(OA)){OA=isStr(OA)?OA.split(s$.NV_DEL).toJson():OA.toJson();} _w.OtherAtts=OA}else{OA=_w.OtherAtts=[]}; return OA }

  // Forms
    ,regForm: function _QC_regForm(sForm){
        var _f=_d.forms[sForm], el
        if(_f==undefined){qErr("rimRegForm error: Cannot find form: "+sForm)}
      // Register getElem
        _f.getElem= function qGetFormElem(sElem){
            var _f=_qclient,oElem=_f[sElem],aEl
            if(oElem && oElem.name==sElem) return oElem
            //Elem name must start with num :(
            aEl=_f.elements
            for(var i=0;i<aEl.length;i++)
                if(aEl[i].name==sElem) return aEl[i]
            qErr("rimGetFormElem err: Cant find '"+sElem+"' in form "+_f.name)
        }
      // Append ids to all elems
        for(var i=0;i<_f.elements.length; i++){el=_f.elements[i]
            el.id=el.name
        }

        return _f
    }

    ,cleanCharsForDebug: function QC_cleanCharsForDebug(s){
        return s$(s).convSpecialCharsToHtml().repList("&nbsp;", "\xA0", " ","\xA0","\n","<br>")
    }

  // Don't have usecases anymore for this but it is cool and useful
  // . Very handy for complex JSON data returns
    ,assertData :function QC_assertData(jDat){
        var t=this, aMft=jDat.manifest,v, r, sMsg

        for(var i=0;i<aMft.length;i++){r=aMft[i]; v=jDat[r.ns]
            try {
                sMsg=false
                if(v == undefined){ sMsg="`"+r.ns+"` is not defined in requestDat." }
                if((sMsg=_jtk.validateVarDType(v,r.dtype,r.ns))){ sMsg="`"+r.ns+"` is not properly defined in requestDat.\nReason: "+sMsg }

                if(sMsg){
                    qErr(sMsg)
                }
            } catch(exc1){
                var sADE, jsonDef=[
                    { ns: "ns", dtype: _jtk.DT_STRING, options: [_jtk.ARG_REQUIRED] },
                    { ns: "dtype", dtype: _jtk.DT_STRING, options: [_jtk.ARG_REQUIRED] },
                    { ns: "desc", dtype: _jtk.DT_STRING, options: [_jtk.ARG_REQUIRED] }
                ]
                if((sADE=_jtk.validateJSONDef(aMft[i],jsonDef,"requestDat.manifest["+i+"]"))){// Error in Args passed
                    qErr("JSON value in requestDat.manifest["+i+"] is incorrectly built.\n"+sADE,exc2); return false
                } else {// Args passed ok
                    qErr(getDebugMsg(exc1)); return false
                }
            }
        }
    }




  // - - - - - - - - - - - - -
  // . User Info
  // - - - - - - - - - - - - -
    ,user : {
         client     : "_qclient"
        ,name       : "user"
        ,dataSpec   : []
        ,groups     : null
        ,roles      : null
        ,fullName   : null
        ,loginId    : null

      // Public Methods
      /* 
      	todo - Make this configurable and not dependent on jsonrpc syntax
      */
        ,loadInfo: function user_loadInfo(){
            var t=_qclient ,user=t.user ,jInfo ,oRec, sRpcRet, sAsrt
//            pc(">>")

            try {
                t.rfcLoadingStart()

              // Load User Info
                sRpcRet=jsonrpc.sjpa.getUserInfo([
                	"tzOffset", s$( ((new Date()).getTimezoneOffset()/60) )
                ]);
//pc(
//     "\n tzOffset = "+( ((new Date()).getTimezoneOffset()/60)*-1 )
//);
                eval("jInfo="+sRpcRet)
//pc(
//     "\n jInfo:\n"+s$.pad(q$.dump(jInfo))
//);
              // Pull Data
                sAsrt = "RPC Ret JSON";
                user.LoginId    = agetParam(sAsrt, jInfo, "loginId", true, true);
                user.FullName   = agetParam(sAsrt, jInfo, "fullName", true, true);
                user.Groups     = agetParam_arr(sAsrt, jInfo, "groups", true);
                user.Roles      = agetParam_arr(sAsrt, jInfo, "roles", true);

              // Finalize
                t.rfcLoadingStop()

//                pc("<<")
                return;

            }catch(e){
                t.rfcLoadingStop()
                qErr("Error Getting User Info",e)
            }
        }
        ,isMember: function user_isMember(aGrpRleUsr){
            var t=_qclient,user=t.user
            if(!isArray(aGrpRleUsr))aGrpRleUsr=[aGrpRleUsr]
            for(var i=0;i<user.Groups.length;i++)if(aGrpRleUsr.has(user.Groups[i]))return true
            for(var i=0;i<user.Roles.length;i++)if(aGrpRleUsr.has(user.Roles[i]))return true
            if(aGrpRleUsr.has(user.LoginId))return true
            return false
        }
        /*
            Notes:
            . Caches Requests for 60 seconds (unless forced)
            . Discovered that IE Caches requests to JSPs
            .. To fix this, added random param to URI
         */
        ,iLastLoginCheck: -1
        ,bIsLoggedInLast: false
        ,isLoggedIn: function user_isLoggedIn(bForceCheck){
//            pc(">>")
            var t=this,sURI,iNow,iSecs

            try {
            	
            	if(!(sCtxChkPath = QClient_config.contextCheckServletPath)){qErr("QClient_config.contextCheckServletPath is BLANK!!")}
            	sURI=contextPath+sCtxChkPath+"?__r="+nRand(4);
                iNow = q$.getTS();
                if(t.iLastLoginCheck>-1){
                    if(bForceCheck){
                        pc("Forcing check If Logged in!");
                    } else {
                        iSecs = (iNow-t.iLastLoginCheck)/1000
//                        pc("iSecs = "+iSecs)
                        if(iSecs<60)return t.bIsLoggedInLast
                    }
                }

                ajax = qCreateHttpRequest();
                ajax.open("GET", sURI, false);
                ajax.send(null);

                var status, statusText, data;
                try {
                    status = ajax.status;
                    statusText = ajax.statusText;
                    data = ajax.responseText;
                } catch(e) {
                    qErr("Connection failed to '"+sURI+"'.");
                }

//pc(
//     "\n (: Cool It Worked :)"
//    +"\n ajax:\n"      + s$.pad(q$.dump(ajax))
//    +"\n status = "    + status
//    +"\n statusText = "    + statusText
//    +"\n data = |"    + data+"|"
//)

                if(data == "true"){
                    t.iLastLoginCheck=iNow
                    return (t.bIsLoggedInLast=true)
                }
                if(data == "false"){
                    t.iLastLoginCheck=iNow
                    return (t.bIsLoggedInLast=false)
                }
                qErr("Invalid boolean return from RPC: '"+data+"'");

//                pc("<<")
            }catch(e){
                pc(
                       " Page Failed!"
                    +"\n Message = "        + s$.pad(e.message)
                    +"\n ajax:\n"           + s$.pad(q$.dump(ajax))
                )
            }

        }

    }

    ,jsloader: {
      // Properties
      // . Private
      // . Active Loads
        _activeLoads: [],_KEY:0,_URL:1,_CB:2,_ARGS:3,_TIME_RQ:4,_TIME_ST:5,_TIME_FN:6
       ,_alIndex : {}
       ,_inter: null
       ,_maxTO_start: 1000*30// 30 seconds
       ,_maxTO_load: 1000*60*2// 1 minutes
      // . Public
      // . (none)
      // Methods
      // . Public
       ,loadJS: function jsload_loadJS(){
//            pc("called")
            var t=this, args=arguments, sKey, sUrl, fCB, aCBArgs, scr
            sKey  = agetParam       ("args (sKey)" , args, "0", true, true)
            sUrl  = agetParam       ("args (sUrl)" , args, "1", true, true)
            fCB   = agetParam_func  ("args (fCB)"  , args, "2", false)
            aCBArgs = agetParam_arr ("args (aCBArgs)"  , args, "3", false)
            if(t._alIndex[sKey]){pc("JS Already Loading: "+sKey); return}
            t._activeLoads.p([sKey, sUrl, fCB, aCBArgs, new Date(), null])
        }
      // . Private
        ,start: function jsload_start(){
//            pc("called")
            var t=this,a=t._activeLoads,a2=[],r,oH=_d.getElementsByTagName('head')[0]
            if(isDef(t._inter))return
            for(var i=0;i<a.length;i++)a2[i]=a[i]//<~ Fix Goofy IE Issue!
            for(var i=0;i<a2.length;i++) {r=a2[i]
//pc(
//    "\n a i = "+i
//   +"\n t._KEY = "+r[t._KEY]
//   +"\n t._URL = "+r[t._URL]
////   +"\n len="+a2.length
//
//)
              // Load JS Library
                scr = _d.createElement('script');
                scr.type = 'text/javascript';
                scr.name = r[t._KEY];
//pc("b i = "+i+", t._KEY = "+r[t._KEY]+", len="+a2.length)
                scr.src = r[t._URL];
//pc("c i = "+i+", t._KEY = "+r[t._KEY]+", len="+a2.length)
                oH.appendChild(scr);
//pc("d i = "+i+", t._KEY = "+r[t._KEY]+", len="+a2.length)
            }
            t._inter = setInterval("_qclient.jsloader._check_to()", 1000)
        }
        ,_stop: function jsload_stop(){
//            pc("called")
            var t=this
            clearInterval(t._inter); t._inter=null
        }
        ,_check_to: function jsload_check_to(){
            var t=this,a=t._activeLoads,r,iNow,iElaps
            if(a.length==0){ t._stop(); return }
            iNow = q$.getTS()
            for(var i=0;i<a.length;i++){r=a[i]
                if(r[t._TIME_ST]){// Has Started laoding
                    iElaps = iNow - r[t._TIME_ST].getTime()
                    if(iElaps>t._maxTO_load){
                        clearInterval(t._inter); t._inter=null
                        qErr("Loading of JS Lib Timed Out. sKey="+r[t._KEY]+", sURL='"+r[t._URL]+"'. Load Started but not complete!")
                    }
                } else {// Has NOT started loading
                    iElaps = iNow - r[t._TIME_RQ].getTime()
                    if(iElaps>t._maxTO_start){
                        clearInterval(t._inter); t._inter=null
                        qErr("Loading of JS Lib Timed Out:  sKey="+r[t._KEY]+", sURL='"+r[t._URL]+"'.")
                    }
                }
            }
        }
        ,_isLastWithCB: function jsload__isLastWithCB(fCB, sKey){
//            pc("called")
            var t=this,a=t._activeLoads,r
            for(var i=0;i<a.length;i++){r=a[i]

//pc(
//    "\n i = "+i
//   +"\n sKey = "+sKey
//   +"\n a.len = "+a.length
//   +"\n r[t._KEY] = "+r[t._KEY]
//   +"\n f eq = "+(r[t._CB] == fCB)
//);


                if(r[t._KEY]!=sKey && r[t._CB] == fCB){
//                      pc("** Is *not* last cb");
                      return false
                }
            }
            return true;
        }
        ,_libLoaded: function jsload_libLoaded(){
//pc("called - "+arguments[0]+", "+arguments[1])
            var t=this, args=arguments, sKey, sUrl, fCB, aCBArgs, a=t._activeLoads,r, _fnDyn
            sKey = agetParam("args (sKey)", args, "0", true, true)
            bIsStart = agetParam("args (bIsStart)", args, "1", true)
            for(var i=0;i<a.length;i++){r=a[i]
                if(r[t._KEY]==sKey){
                    if(bIsStart){
                        r[t._TIME_ST]=new Date()
                    } else {
                        r[t._TIME_FN]=new Date()
                        fCB=r[t._CB];
                        if(t._isLastWithCB(fCB, sKey))
                            fCB(r[t._ARGS], r)
                        a.splice(i,1); t._alIndex[sKey]=null
                    }
                    return
                }
            }
            qErr("Key Not Found: "+sKey);
        }

    }
    ,constructor: function jsload_ctor(j) {this.base_constr(j)}

});
/* Default QClient - For special use. Eg Widget Windows */
DefQClient = QClient.extend({
     className: "DefQClient"
    ,base_constr : function defQClient_base_constr(){
//        pc(">>")
        var t=this
        try {
            t.base({instance: t, name:"defaultQClient", qcName:"_dqc"})
        }catch(x){
            qErr(
                t.className+" Base Constructor Failed: "
                ,x
            )
        }

//      pc("<<")
    }
    
  // Load Validation
    ,validateLoad :function qcdef_validateLoad(SR,OA){
//        pc(">>")
        _qclient.base(SR,OA)
        var
            t=_qclient
            ,bSPFailed = agetParam_bool("ServerReport", SR, "SPFailed", false)
            ,sSPErr = agetParam("ServerReport", SR, "SPError", false, false)
            ,sErr="",sMsg=""
            ,jSRFlags={
                 SPFailed: "core", SPError: "core",LoggedIn: "core", ParamDump: "core", ServerType: "core"
                ,GeneralError:"qLibJS Error:<br>~1"
                ,GeneralMessage:"qLibJS General Message:<br><br>~1"
                    +"<div align=left><br><br>Options:"
                        +"<div style='margin: 3px 0px 0px 10px'>&#149;&nbsp;<a href='javascript: _w.close()'>Close window</a></div>"
                    +"</div>"
            }



        if(bSPFailed){
            sErr=sSPErr
            t.loadHadCoreError=true;
        }else if(v=SR["GeneralMessage"]){
            alert(jSRFlags.GeneralMessage.format(v))
//            pc("<<")
            return false
        }else if(isUndef(OA))
            sErr="OtherAtts must be defined!"
        else {
          // Validate Flags
            var aSRN=qObjListProps(SR),sSRM,sSRN
            for(var i=0;i<aSRN.length;i++){ sSRM=jSRFlags[aSRN[i]]
                if(isDef(sSRM)){
                    if(sSRM!="core"){
                        sErr=sSRM.format(SR[aSRN[i]])
                        break
                    }
                }else{
                    sErr="Unexpected and handled ServerReport Flag: \'~1\' = \'~2\'".format(aSRN[i],SR[aSRN[i]]); break
                }
            }
        }

        if(sErr!=""){
            var aPD,sPD=""
            if(aPD=SR["ParamDump"]){sPD=aPD.split("^").group(2).join("<br>")}
            sErr=sErr+"<br><br>OA Dump: ~1~2".format((qObjGet(OA).join(", ")),(sPD==""?"":"<br>Param Dump: <br>"+sPD))
            qErr(sErr)
//            pc("<<")
            return false
        }

        _w.SR=SR
        _w.OA=OA
//        pc("<<")
        return true

    }
    ,priv_errorHandler :function qcdef_priv_errorHandler(dd){
        var t=_qclient, errRC, bHasRC, bHandled;
            
        if(!isQDD(dd))qCoreErr("Core Error. arg[0] is not qDD. ", new Error(), {varDump:{args: q$.dumpArgs()}});  

        try {
        	
            bHandled = t.priv_errorHandlerCore(dd)
            if(_w.bDebug)bHandled=false
            return bHandled

        }catch(x){qErr("CORE ERROR IN priv_errorHandler",x,{origErr: s$.pad(q$.dumpArgs(), ". ")})}
        
    }
    ,constructor: function DefQClient() {this.base_constr()}
});function __initDefQClient(){new DefQClient()}


/* END Default QCLIENT */






/******************************************************
    JS Dynamic Library Load Class

    :: OOP Specs ::
    :: . Constraints ::
    . Many
    :: Direct Known Subclasses ::
    . QBase
        |__QJSLibrary
******************************************************/
QJSDynlib = QBase.extend({
    name: null
    ,constructor: function jsdl_ctor(name){
       var t=this
       if(!name)qErr("Ctor param name is required")
       if(!_w._qclient){qErr("_qclient not loaded!");}
       t.name=name
       _w._qclient.jsloader._libLoaded(this.name, true)
    }
    ,loaded: function jsdl_loaded()  { _w._qclient.jsloader._libLoaded(this.name, false) }
})


/*******************************************************

    Widget Base Class

    :: OOP Specs ::
    ::  . Constraints ::
        . Single Instnace - only one Client per window
    :: Direct Known Subclasses ::
        . Base
            |__QEventDomain
                |__QWidget*

********************************************************/

// Each window should only have one Instance of QClient
QWidget__firstWidget=true
QWidget__staticReg={
    instancePool: {}
}
QWidget = QEventDomain.extend({// Abstract class
  // Constructor
    base_constr :function qwidget_base_constr(instance, name){
//        pc(">>")
        try {
            this.base(instance, name)
            var t=this, reg=_w.QWidget__staticReg,sRand

          // Initialize Widget Registry
            if(_w.QWidget__FirstWidget){
                QWidget__FirstWidget=false
                //... do stuff on first call
            }
        }catch(exc){
            t.loadHadCoreError=true
            qErr(
                "QWidget Base Core Error"
                ,exc
                ,{
                     t          :t
                    ,rawStack   :(is.moz?"\n"+exc.stack:"(na - isie)")
                }
            )
        }
//        pc("<<")

    }


// Constants
// . General

// . Debug

// Properties
// Get Properties (public read)

// Read only (set priv)


});


/********************************************************
    GENERAL GLOBAL FUNCTIONS

********************************************************/

/*
    DOCUMENT LEVEL LISTENERS
*/
_aDoc_KD_Ev=[] // Array of events to fire for doc_keydown event
_aDoc_MD_Ev=[] // Array of events to fire for doc_mousedown event
_aDoc_KD_Ev.p("_WN_Document_Keydown")
function _d_keydown_master(e){e=getEvent(e);
	var a=_aDoc_KD_Ev;
    if(_w._qcGate(e)){
        for(var i=0;i<a.length;i++){
            if(isDef("_w."+a[i])){ eval(a[i]+"(e)") }
        }
    }
}
function _d_mousedown_master(e){e=getEvent(e);
	var a=_aDoc_MD_Ev;
//pc("e = "+e)
    if(_w._qcGate(e)){
        for(var i=0;i<a.length;i++){
            if(isDef("_w."+a[i])){ eval(a[i]+"(e)") }
        }
    }
}
function _WN_Document_Keydown(e){
	var iKC=e.keyCode
//pc("iKC = "+iKC)
    if(e.ctrlKey && e.shiftKey) {
        if([192,120].has(iKC) && _w._qclient) // ~ F9
            _qclient.debugDump()
        else if([79,121].has(iKC)) // O F10
            objDump(_w,"window")
        else if([123].has(iKC)) //  F12
            winQuickRefresh()
        else if([70,119].has(iKC)){ //  F F8
            clearCons()
            pc("Console cleared with ctrl-shift-F")
        }
    }
}

function __initGlobalEvents(){
    try {// DOCUMENT LISTENERS
        assignEventListener(_d,"keydown",_w._d_keydown_master)// Don't use _qclient.addEvent for document!
        assignEventListener(_d,"mousedown",_w._d_mousedown_master)// Don't use _qclient.addEvent for document!
    } catch(e){qErr("Error Doc Events")}
}; __initGlobalEvents()
